import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.HashMap;
import java.util.Map;
import java.util.Scanner;
import java.util.TreeMap;

/**
 * 
 * TextBuddy for CE1
 * Tasks are represented as a string separated by newlines
 * It will be stored sequentially in a .txt ascii file and therefore can be read / editted directly
 * Commands can easily be extended in the initializeCommands method  
 * 
 * @author Koh Zi Chun
 *
 */


public class TextBuddy {

	private interface IActionHandler {
		public boolean executeMethod(String args) throws Exception;
	};

	private class TextBuddyException extends Exception {
		private static final long serialVersionUID = 1L;
		private int errCode;
		
		public TextBuddyException(int errCode){
			super("");
			this.errCode = errCode;
		}
		public int getErrorCode(){
			return errCode;
		}
	}


	private static final String TEXT_BUDDY_USAGE = "Welcome to TextBuddy, your geeky task management tool\n  Usage: java TextBuddy [filename]";
	private static final int TEXT_BUDDY_INVALID_ARGUMENT = 400;
	private static final int TEXT_BUDDY_USER_EXIT = 200;
	private static final String TEXT_BUDDY_COMMAND_PROMPT = "Command: ";
	private static final String TEXT_BUDDY_NO_COMMAND = "Command not recognized.";
    
	static String file_name = null;
	
	static Scanner file_scanner = null;
    static File file_object = null;
    static Scanner stdin = new Scanner(System.in);
    static PrintWriter file_writer = null;
	
    static Map<String, IActionHandler> commands = new HashMap<String,IActionHandler>();
	
	
	
	public static void main(String[] args) {
		try{
			
			checkArguments(args);
			checkAndReadFile(args[0]);
			
			initializeCommands();
			
			programMainLoop();
			
		}catch(TextBuddyException e){
		}catch(IOException e){
			System.out.println("The file you provided is either corrupted or cannot be written to");
		}catch(Exception e){
			System.out.println("TextBuddy has encounted an error and is terminating unexpectedly. ");
			
			e.printStackTrace();
		}
		
	}


	private static Map<Integer, String> task_list = new TreeMap<Integer,String>();
	private static Integer task_index_counter = 0;
	private static void unserializeFile(){
		if (file_scanner == null) return;
		
		String line = null;
		task_index_counter = 0;
		
		while(file_scanner.hasNextLine() && ((line = file_scanner.nextLine()) != null) ) {
			task_list.put(task_index_counter++, line);
		}


	}
	private static void serializeFile() throws IOException{
		file_writer = new PrintWriter( new FileWriter(file_name ));
		
		for(Map.Entry<Integer, String> element : task_list.entrySet()){
			file_writer.println(element.getValue());
		}
		
		file_writer.close();
	}
	
	private static void addTaskAndSaveFile(String task) throws IOException{
		task_list.put( task_index_counter++ , task);
		
		serializeFile();
	}
	private static void deleteTaskByIndexAndSaveFile(int index) throws IOException{
		int count = 0;
		Integer todelete = 0;
		
		// enumerate task_list and find the entry that correspond to the index
		for(Map.Entry<Integer, String> element : task_list.entrySet()){
			++count;
			if (count == index) todelete = element.getKey();
		}
		
		System.out.println( "  deleted from " + file_name+": \""+task_list.get(todelete)+"\"" );
		task_list.remove(todelete);
		
		serializeFile();
	}
	private static void deleteAllTasksAndSaveFile() throws IOException{
		task_list.clear();
		serializeFile();
	}


	private static void initializeCommands() {
		commands.put("add", new IActionHandler(){
			public boolean executeMethod(String args) throws IOException{
				addTaskAndSaveFile(args);
				System.out.println("  added to "+file_name+": \""+args+"\"");
				
				return false;
			}
		});

		commands.put("display", new IActionHandler(){
			public boolean executeMethod(String args){
				if (task_list.size() > 0){
					Integer count = 1;
					for(Map.Entry<Integer, String> element : task_list.entrySet()){
						System.out.println("  " + (count++) + ". " + element.getValue());
					}
				}else{
					System.out.println("  "+file_name+" is empty!");
				}
				
				return false;
			}
		});

		commands.put("delete", new IActionHandler(){
			public boolean executeMethod(String args) throws IOException{
				try{
					int index = Integer.parseInt(args);
					if (index >= 1 && index <= task_list.size()){
						deleteTaskByIndexAndSaveFile(index);
					}
					return false;
				}catch(NumberFormatException e){
					
				}
				System.out.println("  Invalid task index!");
				
				
				return false;
			}
		});

		commands.put("clear", new IActionHandler(){
			public boolean executeMethod(String args) throws IOException{
				deleteAllTasksAndSaveFile();
				System.out.println("  all content deleted from "+file_name);
				return false;
			}
		});

		commands.put("exit", new IActionHandler(){
			public boolean executeMethod(String args){				
				System.out.println("  Thank you for using Text Buddy!");
				return true;
			}
		});

	}





	private static void programMainLoop() throws Exception {
		String command = null;
		String arguments = null; 
		boolean terminate = false;
		
		while(terminate == false){
			System.out.print(TEXT_BUDDY_COMMAND_PROMPT);
			
			if (stdin.hasNext() == false) break;
	        String line = stdin.nextLine();

			String[] tokens = line.split("\\s+",2);
			arguments = (tokens.length > 1 ? tokens[1] : "");
			command = tokens[0];

			
			if (commands.containsKey(command)){
				terminate = commands.get(command).executeMethod(arguments);
			}else{
				System.out.println(TEXT_BUDDY_NO_COMMAND);
			}
		}
	}


	private static void checkAndReadFile(String filename) throws IOException {
		file_name = filename;

		file_object = new File(file_name);
		if (!file_object.exists()){
			file_object.createNewFile();
		}
		file_scanner = new Scanner(file_object);
		
		unserializeFile();
		
		file_scanner.close();
	}





	private static void checkArguments(String[] args) throws TextBuddyException {
		if (args.length != 1) {
			System.out.println(TEXT_BUDDY_USAGE);
			throw (new TextBuddy()).new TextBuddyException(TEXT_BUDDY_INVALID_ARGUMENT);
		}
	}

}
